package com.h3c.ptability.utils;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.h3c.it.common.misc.lang.Assert;
import com.h3c.ptability.annotation.FieldCheck;
import com.h3c.ptability.enums.CheckType;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author :h24002
 * @date :2022/10/27 14:32
 * @description :FieldUtils
 */
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class FieldUtils {

    public static Pattern humpPattern = Pattern.compile("[A-Z]");


    /**
     * 检查实体中指定字段是否为空，字段必须有@FieldCheck注解，不在检查字段内的跳过
     *
     * @param t           被检查实体
     * @param checkFields 自定义检查字段(英文逗号分隔)
     * @return T
     * @author z22968
     * @date 2022-03-24 09:30
     */
    public static <T> T checkFields(T t, String checkFields) {
        // 没有则为空列表
        List<String> checkFieldList = new ArrayList<>();
        if (StringUtils.hasText(checkFields)) {
            checkFieldList = Arrays.asList(checkFields.split(","));
        }
        return checkFields(t, checkFieldList);
    }

    /**
     * 检查实体中有@FieldCheck注解的字段是否为空，只检查传入字段
     *
     * @param t             被检查实体
     * @param checkFieldArr 检查字段数组
     * @return T
     * @author z22968
     * @date 2022-03-24 15:28
     */
    @SafeVarargs
    public static <T> T checkFields(T t, SFunction<T, ?>... checkFieldArr) {
        List<SFunction<T, ?>> tmp = ListUtil.toList(checkFieldArr);
        List<String> checkFieldList = new ArrayList<>();
        for (SFunction<T, ?> unCheckField : tmp) {
            String fieldName = getFieldName(unCheckField, false);
            checkFieldList.add(fieldName);
        }
        return checkFields(t, checkFieldList);
    }

    /**
     * 检查实体中有@FieldCheck注解的字段是否为空，只检查传入字段
     *
     * @param t              被检查实体
     * @param checkFieldList 检查字段List
     * @return T
     * @author z22968
     * @date 2022-03-24 15:28
     */
    public static <T> T checkFields(T t, List<String> checkFieldList) {
        Class<?> c = t.getClass();
        // 获取全部属性进行遍历
        Field[] declaredFields = ReflectUtil.getFields(c);
        for (Field field : declaredFields) {
            field.setAccessible(true);
            String name = field.getName();
            FieldCheck annotation = field.getAnnotation(FieldCheck.class);
            // 没注解 / 不在检查字段内 ，跳过
            if (annotation == null || (checkFieldList.size() > 0 && !checkFieldList.contains(name))) {
                continue;
            }
            checkField(t, field, annotation);
        }
        return t;
    }

    /**
     * 检查实体中有@FieldCheck注解的字段是否为空，排除传入字段
     *
     * @param t               被检查实体
     * @param unCheckFieldArr 不检查字段数组
     * @return T
     * @author z22968
     * @date 2022-03-24 15:28
     */
    @SafeVarargs
    public static <T> T checkFieldsExclude(T t, SFunction<T, ?>... unCheckFieldArr) {
        List<SFunction<T, ?>> tmp = ListUtil.toList(unCheckFieldArr);
        Assert.notEmpty(tmp, CommonUtil.errorMsg("不检查字段不能为空!"));
        List<String> unCheckFieldList = new ArrayList<>();
        for (SFunction<T, ?> unCheckField : tmp) {
            String fieldName = getFieldName(unCheckField, false);
            unCheckFieldList.add(fieldName);
        }
        return checkFieldsExclude(t, unCheckFieldList);
    }

    /**
     * 检查实体中有@FieldCheck注解的字段是否为空，排除传入字段
     *
     * @param t             被检查实体
     * @param unCheckFields 不检查字段(英文逗号分隔)
     * @return T
     * @author z22968
     * @date 2022-03-24 15:28
     */
    public static <T> T checkFieldsExclude(T t, String unCheckFields) {
        Assert.hasText(unCheckFields, CommonUtil.errorMsg("非检查字段不能为空！"));
        // 没有则为空列表
        List<String> unCheckFieldList = Arrays.asList(unCheckFields.split(","));
        return checkFieldsExclude(t, unCheckFieldList);
    }

    /**
     * 检查实体中有@FieldCheck注解的字段是否为空，排除传入字段
     *
     * @param t                被检查实体
     * @param unCheckFieldList 不检查字段
     * @return T
     * @author z22968
     * @date 2022-03-24 15:28
     */
    public static <T> T checkFieldsExclude(T t, List<String> unCheckFieldList) {
        Assert.notNull(unCheckFieldList, CommonUtil.errorMsg("非检查字段列表不能为空！"));
        Class<?> c = t.getClass();
        // 获取全部属性进行遍历
        Field[] declaredFields = ReflectUtil.getFields(c);
        for (Field field : declaredFields) {
            field.setAccessible(true);
            String name = field.getName();
            FieldCheck annotation = field.getAnnotation(FieldCheck.class);
            //  没注解 / 在非检查字段内，跳过
            if (annotation == null || unCheckFieldList.contains(name)) {
                continue;
            }
            checkField(t, field, annotation);
        }
        return t;
    }

    /**
     * 检查实体字段值是否符合
     *
     * @param t          被检查实体
     * @param field      检查字段
     * @param annotation 检查类型
     * @author z22968
     * @date 2022-03-20 10:28
     */
    private static <T> void checkField(T t, Field field, FieldCheck annotation) {
        String name = field.getName();
        // 获取CheckType值
        CheckType type = annotation.type();
        String desc = annotation.desc();
        if (type == CheckType.notNull) {
            try {
                Object value = field.get(t);
                Assert.notNull(value, CommonUtil.errorMsg((StringUtils.hasText(desc) ? desc : name) + "字段不能为空"));
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        if (type == CheckType.hasText) {
            try {
                Object value = field.get(t);
                if (value != null && value.getClass() != String.class) {
                    throw new AppBusinessException("注解不适用, " + name + "字段必须为String类型");
                }
                Assert.hasText((String) value, CommonUtil.errorMsg((StringUtils.hasText(desc) ? desc : name) + "字段不能为空"));
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        if (type == CheckType.notEmpty) {
            try {
                String fieldType = field.getType().getName();
                Object value = field.get(t);
                if (value != null && "java.util.List".equals(fieldType)) {
                    Assert.notEmpty((Collection<?>) value, CommonUtil.errorMsg((StringUtils.hasText(desc) ? desc : name) + "字段不能为空"));
                } else {
                    throw new AppBusinessException("注解不适用, " + "不支持 " + value + " 类型判断");
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 驼峰转下划线
     *
     * @param str 驼峰字符串
     * @return java.lang.String
     * @author z22968
     * @date 2022-03-22 10:45
     */
    public static String humpToLine(String str) {
        Matcher matcher = humpPattern.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, "_" + matcher.group(0).toLowerCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    /**
     * lambda转实体字段名
     *
     * @param column lambda表达式
     * @return java.lang.String
     * @author z22968
     * @date 2022-07-22 16:57
     */
    public static <T> String getFieldName(SFunction<T, ?> column) {
        return getFieldName(column, false);
    }

    /**
     * lambda转类的数据库字段名/实体属性名
     *
     * @param column    lambda表达式
     * @param useDbName 使用数据库字段名
     * @return java.lang.String
     * @author z22968
     * @date 2022-07-22 16:06
     */
    @SneakyThrows
    public static <T> String getFieldName(SFunction<T, ?> column, boolean useDbName) {
        // 获取lambda表达式的序列化形式
        SerializedLambda serializedLambda = getSerializedLambda(column);

        // 获取方法名，去掉get/set/is
        String fieldName;
        if (serializedLambda.getImplMethodName().startsWith("is")) {
            fieldName = serializedLambda.getImplMethodName().substring("is".length());
        } else {
            fieldName = serializedLambda.getImplMethodName().substring("get".length());
        }
        // 首字母小写
        fieldName = fieldName.replaceFirst(fieldName.charAt(0) + "", (fieldName.charAt(0) + "").toLowerCase());
        if (!useDbName) {
            return fieldName;
        }
        // 获取实体类
        Field field = Class.forName(serializedLambda.getImplClass().replace("/", ".")).getDeclaredField(fieldName);
        // 从@TableField中获取
        TableField tableField = field.getAnnotation(TableField.class);
        if (tableField != null && tableField.value().length() > 0) {
            return tableField.value();
        }
        // 从TableId中获取
        TableId tableId = field.getAnnotation(TableId.class);
        if (tableId != null && tableId.value().length() > 0) {
            return tableId.value();
        }
        return fieldName;
    }

    /**
     * 获取lambda表达式的序列化形式
     *
     * @param column lambda表达式
     * @return java.lang.invoke.SerializedLambda
     * @author z22968
     * @date 2022-07-22 16:16
     */
    private static <T> SerializedLambda getSerializedLambda(SFunction<T, ?> column) {
        try {
            // 获取writeReplace方法
            Method method = column.getClass().getDeclaredMethod("writeReplace");
            // 设置可访问
            method.setAccessible(true);
            // 调用writeReplace方法获取lambda表达式的序列化形式
            return (SerializedLambda) method.invoke(column);
        } catch (ReflectiveOperationException e) {
            throw new RuntimeException(e);
        }
    }
}

